#include <STDIO.H>
#include <LIMITS.H>
#include <WINDOWS.H>
#include "directio.h"
#include "Server\prime.h"

/* If we found a prime then return PRIME, otherwise return NOT_PRIME */

#define PRIME           1
#define NOT_PRIME       0

/* Coordinates for displaying numbers to be tested */

#define TESTING_X1      3
#define TESTING_Y1      3
#define TESTING_X2     23
#define TESTING_Y2     18

/* Coordinates for displaying prime numbers */

#define PRIME_X1       30
#define PRIME_Y1        3
#define PRIME_X2       50
#define PRIME_Y2       18

#define ERROR_EXIT      2
#define SUCCESS_EXIT    0
#define STRING_LENGTH 256

/* Delay time in microseconds */

#define WAIT          350
#define WAIT_DISPLAY 2000

/* Colors used in this application */

#define WHITE_ON_BLUE FOREGROUND_WHITE|FOREGROUND_INTENSITY|BACKGROUND_BLUE
#define WHITE_ON_CYAN FOREGROUND_WHITE|FOREGROUND_INTENSITY|BACKGROUND_CYAN
#define RED_ON_BLUE   FOREGROUND_RED|FOREGROUND_INTENSITY|BACKGROUND_BLUE
#define RED_ON_CYAN   FOREGROUND_RED|FOREGROUND_INTENSITY|BACKGROUND_CYAN
#define CYAN_ON_CYAN  FOREGROUND_CYAN|BACKGROUND_CYAN
#define BLUE_ON_CYAN  FOREGROUND_BLUE|FOREGROUND_INTENSITY|BACKGROUND_CYAN

/* Max number of threads for this application */

#define MAX_THREADS      15
#define COMP_NAME_LENGTH 7

int NumThreads;

/* Prototypes of functions used in this application */

unsigned char IsPrime(unsigned long TestNumber);
extern char ServerStatus(char);
extern void thread_local(void);
extern void thread_remote(int count);
extern void Usage(void);
extern void InitializeApplication(void);
extern void NotifyServer(char);
extern void thread_client(void);
char IsActiveServer[MAX_THREADS];

DWORD  lpIDThreadClient;
HANDLE hthread_client;

/* Critical Section for choosing next available number for testing */

CRITICAL_SECTION GlobalCriticalSection;

/* Storage for Computer Name */

char computer_name_buffer[MAX_COMPUTERNAME_LENGTH];

/* Unique number returned by the server */

unsigned long PrimeServerHandle[MAX_THREADS];

/*
NextNumber    - Next available number to test
StartTime     - Time that we start to compute primes
CurrTime      - Current Computer time
NoPrimeLocal  - Number of primes computed locally
NoPrimeRemote - Number of primes computed on the remote computer
StartNumber   - The starting number for testing primes
*/

unsigned long NextNumber = 1, StartTime, CurrTime, NoPrimeLocal,
   NoPrimeRemoteT, StartNumber = 1;

handle_t BindingHandle[MAX_THREADS];
unsigned long NoPrimeRemote[MAX_THREADS];

PCONTEXT_HANDLE_TYPE phContext[MAX_THREADS];

int ipszNetAdd = 0;

RPC_NS_HANDLE ImportContext;

unsigned char pszRemoteName[MAX_THREADS][MAX_COMPUTERNAME_LENGTH];

#define REMOTE_TRY 100

unsigned char Notry[MAX_THREADS];

void main(int argc, char **argv)
   {
   RPC_STATUS status; /* returned by RPC API function */

   unsigned char *pszUuid                          = NULL;
   unsigned char *pszProtocolSequence              = "ncacn_np";
   unsigned char *pszNetworkAddress[MAX_THREADS]   = { NULL };
   unsigned char *pszEndpoint[MAX_THREADS]         =
      {
      "\\pipe\\prime", "\\pipe\\prime", "\\pipe\\prime", "\\pipe\\prime",
      "\\pipe\\prime", "\\pipe\\prime", "\\pipe\\prime", "\\pipe\\prime",
      "\\pipe\\prime"
      };
   unsigned char *pszOptions                       = NULL;
   unsigned char *pszStringBinding[MAX_THREADS]    = { NULL };

   DWORD  Max_ComputerName_Length = MAX_COMPUTERNAME_LENGTH;
   DWORD  lpIDThread[MAX_THREADS];
   HANDLE hthread_remote[MAX_THREADS];

   int count;

   /* Allow the user to override settings with command line switches */

   for(count = 1; count < argc; count++)
      {
      if((*argv[count] == '-') || (*argv[count] == '/'))
         {
         switch(tolower(*(argv[count]+1)))
            {
            case 'p': /* protocol sequence */
               pszProtocolSequence = argv[++count];
               break;

            case 'n': /* network address */
               {
               char tokensep[] = " \t,;", *token;
               token = strtok(argv[++count], tokensep);

               while(token != NULL)
                  {
                  pszNetworkAddress[ipszNetAdd] = token;
                  token = strtok(NULL, tokensep);
                  strcpy(pszRemoteName[ipszNetAdd],
                     strupr(&pszNetworkAddress[ipszNetAdd][2]));
                  pszRemoteName[ipszNetAdd][COMP_NAME_LENGTH] = 0;
                  ipszNetAdd++;
                  }
               }
               break;

            case 'e':
               {
               char tokensep[] = " \t,;", *token;
               token =strtok(argv[++count], tokensep);

               while(token != NULL)
                  {
                  pszEndpoint[ipszNetAdd] = token;
                  token = strtok(NULL, tokensep);
                  ipszNetAdd++;
                  }
               }
               break;

            case 'o':
               pszOptions = argv[++count];
               break;

            case 'f': /* first number */
               NextNumber = StartNumber = atol(argv[++count]);
               break;

            case 't': /* number of threads */
               NumThreads = atoi(argv[++count]);
               if(NumThreads > MAX_THREADS)
                  NumThreads = MAX_THREADS;
               break;

            case 'h':
            case '?':
            default:
               Usage();
            }
         }
      else
         Usage();
      }

   if(pszNetworkAddress)
      {
      int i;

      /*
      Use a convenience function to concatenate the elements of
      the string binding into the proper sequence
      */

      for(i = 0; i < ipszNetAdd; i++)
         {
         status = RpcStringBindingCompose(
            pszUuid,
            pszProtocolSequence,
            pszNetworkAddress[i],
            pszEndpoint[i],
            pszOptions,
            &pszStringBinding[i]);

         if(status)
            exit(ERROR_EXIT);
         }

      /* Set the binding handle that will be used to bind to the server */

      for(i = 0; i < ipszNetAdd; i++)
         {
         status = RpcBindingFromStringBinding(pszStringBinding[i],
            &BindingHandle[i]);

         if(status)
            exit(ERROR_EXIT);
         }
      }

   GetComputerName(computer_name_buffer, &Max_ComputerName_Length);

   InitializeCriticalSection(&GlobalCriticalSection);

      {
      char i;

      for(i = 0; i < ipszNetAdd; i++)
         ServerStatus(i);
      }

   for(count = 1; count <= ipszNetAdd ; count++)
       hthread_remote[count-1] = CreateThread(NULL, 0,
          (LPTHREAD_START_ROUTINE)thread_remote, (LPVOID)count, 0,
          &lpIDThread[count-1]);

   InitializeApplication();

   hthread_client = CreateThread(NULL, 0,
      (LPTHREAD_START_ROUTINE)thread_client, NULL,
      CREATE_SUSPENDED, &lpIDThreadClient);

   SetThreadPriority(hthread_client, THREAD_PRIORITY_NORMAL);
   ResumeThread(hthread_client);
   thread_local();
   DeleteCriticalSection(&GlobalCriticalSection);

   /*
   The calls to the remote procedures are complete
   Free the string and the binding handle
   */

   /* remote calls done - unbind */

   if(pszNetworkAddress)
      {
      int i;

      for(i = 0; i < ipszNetAdd; i++)
         {
         status = RpcStringFree(&pszStringBinding[i]);

         if(status)
            exit(ERROR_EXIT);
         }
      }

   exit(SUCCESS_EXIT);
   }

void thread_local(void)
   {
   unsigned long temp;
   char Buffer[STRING_LENGTH];
   int loop;

   SMALL_RECT psrctScrollRectTesting, psrctScrollRectPrime;
   COORD coordDestOriginTesting, coordDestOriginPrime;
   CHAR_INFO pchiFill;
   WORD Color[TESTING_X2-TESTING_X1-3], Normal[TESTING_X2-TESTING_X1-3];

   COORD ComputTestCoord;
   COORD ComputPrimeCoord;

   DWORD dummy;
   COORD PrimeCoord;

   psrctScrollRectTesting.Left   = TESTING_X1+1;
   psrctScrollRectTesting.Top    = TESTING_Y1+2;
   psrctScrollRectTesting.Right  = TESTING_X2-1;
   psrctScrollRectTesting.Bottom = TESTING_Y2-1;

   coordDestOriginTesting.X = TESTING_X1+1;
   coordDestOriginTesting.Y = TESTING_Y1+1;

   psrctScrollRectPrime.Left   = PRIME_X1+1;
   psrctScrollRectPrime.Top    = PRIME_Y1+2;
   psrctScrollRectPrime.Right  = PRIME_X2-1;
   psrctScrollRectPrime.Bottom = PRIME_Y2-1;

   coordDestOriginPrime.X = PRIME_X1+1;
   coordDestOriginPrime.Y = PRIME_Y1+1;

   pchiFill.Char.AsciiChar = (char)32;
   pchiFill.Attributes = WHITE_ON_BLUE;

   ComputTestCoord.X  = TESTING_X2-7;
   ComputTestCoord.Y  = TESTING_Y2-1;
   ComputPrimeCoord.X = PRIME_X2-7;
   ComputPrimeCoord.Y = PRIME_Y2-1;

   for(loop = 0; loop < TESTING_X2-TESTING_X1-3; loop++)
      {
      Color[loop]  = RED_ON_BLUE;
      Normal[loop] = WHITE_ON_BLUE;
      }

   while(1)
      {
      EnterCriticalSection(&GlobalCriticalSection);
      if((temp = ++NextNumber) >= ULONG_MAX)
         break;
      LeaveCriticalSection(&GlobalCriticalSection);

      EnterCriticalSection(&GlobalCriticalSection);
      ScrollConsoleScreenBuffer(hStdOut, &psrctScrollRectTesting, NULL,
         coordDestOriginTesting, &pchiFill);
      sprintf(Buffer, "%d", temp - 1);
      mxyputs((unsigned char)TESTING_X1+2, (unsigned char)(TESTING_Y2-1),
         Buffer, WHITE_ON_BLUE);
      WriteConsoleOutputAttribute(hStdOut, Normal, 7, ComputTestCoord,
         &dummy);
      mxyputs((unsigned char)(TESTING_X2-7), (unsigned char)TESTING_Y2-1,
         "LOCAL ", WHITE_ON_BLUE);
      LeaveCriticalSection(&GlobalCriticalSection);

      if(IsPrime(temp - 1) != 0)
         {
         PrimeCoord.X = PRIME_X1 + 2;
         PrimeCoord.Y = PRIME_Y2 - 1;

         EnterCriticalSection(&GlobalCriticalSection);
         ScrollConsoleScreenBuffer(hStdOut, &psrctScrollRectPrime, NULL,
            coordDestOriginPrime, &pchiFill);
         sprintf(Buffer, "%-17d", temp - 1);
         mxyputs((unsigned char)(PRIME_X1+2), (unsigned char)PRIME_Y2-1,
            Buffer, WHITE_ON_BLUE);
         WriteConsoleOutputAttribute(hStdOut, Normal, 7, ComputPrimeCoord,
            &dummy);
         mxyputs((unsigned char)(PRIME_X2-7), (unsigned char)PRIME_Y2-1,
            "LOCAL ", WHITE_ON_BLUE);
         LeaveCriticalSection(&GlobalCriticalSection);
         NoPrimeLocal++;
         }
      }
   }

void thread_remote(int count)
   {
   unsigned long temp;
   char Buffer[STRING_LENGTH];
   int loop;

   SMALL_RECT psrctScrollRectTesting, psrctScrollRectPrime;
   COORD coordDestOriginTesting, coordDestOriginPrime;
   CHAR_INFO pchiFill;
   WORD Color[TESTING_X2-TESTING_X1-3], Normal[TESTING_X2-TESTING_X1-3];

   COORD ComputTestCoord;
   COORD ComputPrimeCoord;

   DWORD dummy;
   COORD PrimeCoord;

   psrctScrollRectTesting.Left   = TESTING_X1+1;
   psrctScrollRectTesting.Top    = TESTING_Y1+2;
   psrctScrollRectTesting.Right  = TESTING_X2-1;
   psrctScrollRectTesting.Bottom = TESTING_Y2-1;

   coordDestOriginTesting.X = TESTING_X1+1;
   coordDestOriginTesting.Y = TESTING_Y1+1;

   psrctScrollRectPrime.Left   = PRIME_X1+1;
   psrctScrollRectPrime.Top    = PRIME_Y1+2;
   psrctScrollRectPrime.Right  = PRIME_X2-1;
   psrctScrollRectPrime.Bottom = PRIME_Y2-1;

   coordDestOriginPrime.X = PRIME_X1+1;
   coordDestOriginPrime.Y = PRIME_Y1+1;

   pchiFill.Char.AsciiChar = (char)32;
   pchiFill.Attributes = WHITE_ON_BLUE;

   ComputTestCoord.X  = TESTING_X2-7;
   ComputTestCoord.Y  = TESTING_Y2-1;
   ComputPrimeCoord.X = PRIME_X2-7;
   ComputPrimeCoord.Y = PRIME_Y2-1;

   for(loop = 0; loop < TESTING_X2-TESTING_X1-3; loop++)
      {
      Color[loop]  = RED_ON_BLUE;
      Normal[loop] = WHITE_ON_BLUE;
      }

   while(1)
      {
      if(!IsActiveServer[count-1])
         {
         if(Notry[count-1]++ > REMOTE_TRY)
            {  
            Notry[count - 1] = 0;
            EnterCriticalSection(&GlobalCriticalSection);
             ServerStatus((char)(count - 1));
            LeaveCriticalSection(&GlobalCriticalSection);
            }
         else        
            continue;
         }   
      if(!IsActiveServer[count-1])
         continue;       

/* To make sure that we won't test the same number twice */

      EnterCriticalSection(&GlobalCriticalSection);
      if((temp = ++NextNumber) >= ULONG_MAX)
         break;
      LeaveCriticalSection(&GlobalCriticalSection);

      EnterCriticalSection(&GlobalCriticalSection);
      ScrollConsoleScreenBuffer(hStdOut, &psrctScrollRectTesting, NULL,
         coordDestOriginTesting, &pchiFill);
      sprintf(Buffer, " %-17d", temp - 1);
      mxyputs((unsigned char)TESTING_X1+1, (unsigned char)(TESTING_Y2-1),
         Buffer, RED_ON_BLUE);
      WriteConsoleOutputAttribute(hStdOut, Color, 7, ComputTestCoord,
         &dummy);
      mxyputs((unsigned char)(TESTING_X2-7), (unsigned char)TESTING_Y2-1,
         pszRemoteName[count-1], RED_ON_BLUE);
      LeaveCriticalSection(&GlobalCriticalSection);

      RpcTryExcept
         {
         if(RemoteIsPrime(BindingHandle[count-1], PrimeServerHandle[count-1],
            temp - 1) != 0)
            {
            EnterCriticalSection(&GlobalCriticalSection);
            PrimeCoord.X = TESTING_X1 + 2;
            LeaveCriticalSection(&GlobalCriticalSection);

            PrimeCoord.X = PRIME_X1 + 2;
            PrimeCoord.Y = PRIME_Y2 - 1;

            EnterCriticalSection(&GlobalCriticalSection);
            ScrollConsoleScreenBuffer(hStdOut, &psrctScrollRectPrime, NULL,
               coordDestOriginPrime, &pchiFill);
            sprintf(Buffer, " %-17d", temp - 1);
            mxyputs((unsigned char)(PRIME_X1+1), (unsigned char)PRIME_Y2-1,
               Buffer, RED_ON_BLUE);
            WriteConsoleOutputAttribute(hStdOut, Color, 7, ComputPrimeCoord,
               &dummy);
            mxyputs((unsigned char)(PRIME_X2-7), (unsigned char)PRIME_Y2-1,
               pszRemoteName[count-1], RED_ON_BLUE);
            LeaveCriticalSection(&GlobalCriticalSection);
               NoPrimeRemote[count-1]++;
            NoPrimeRemoteT++;
            }
         }
      RpcExcept(1)
         {
         /* if exceptions occurs */

         EnterCriticalSection(&GlobalCriticalSection);
         ServerStatus((char)(count - 1));
         LeaveCriticalSection(&GlobalCriticalSection);
         }
      RpcEndExcept
      }
   }

/* Tests for prime number on the local computer */

unsigned char IsPrime(unsigned long TestNumber)
   {
   unsigned long count;
   unsigned long HalfNumber = TestNumber / 2 + 1;

   for(count = 2; count < HalfNumber; count++)
      if(TestNumber % count == 0)
         return NOT_PRIME;

   return PRIME;
   }

/* Displays command line options */

void Usage(void)
   {
   printf("\nDistributed client/server prime number example.\n\n");
   printf("Usage: PRIME\n");
   printf(" -p protocol_sequence\n");
   printf(" -n network_address\n");
   printf(" -e endpoint\n");
   printf(" -o options\n");
   printf(" -f first number\n");
   exit(1);
   }

/* Screen Initialization and colors and title lines */

void InitializeApplication(void)
   {
   set_vid_mem();

   clearscreen(BACKGROUND_CYAN);
   StartTime = GetTickCount();

   box(0, 0, 79, 24, DOUBLE);
   mxyputs(37, 0, " PRIMEC ", WHITE_ON_CYAN);

   mxyputs(TESTING_X1, 2, "Testing...", WHITE_ON_CYAN);
   box(TESTING_X1, TESTING_Y1, TESTING_X2, TESTING_Y2, SINGLE);

   mxyputs(PRIME_X1, 2, "Prime!",WHITE_ON_CYAN);
   box(PRIME_X1, PRIME_Y1, PRIME_X2, PRIME_Y2, SINGLE);
   mxyputs(32, 23, "Press Esc to exit", WHITE_ON_CYAN);
   }

/*
   Check the Server status if it is of line, then try to connect, return
   server status
*/

char ServerStatus(char iserver)
   {
   char value = FALSE;

   RpcTryExcept
      {
      PrimeServerHandle[iserver] = InitializePrimeServer(
         BindingHandle[iserver], &phContext[iserver], computer_name_buffer);
      IsActiveServer[iserver] = TRUE;
      }      
   RpcExcept(1)
      {
      value = TRUE;
      IsActiveServer[iserver] = FALSE;
      }
   RpcEndExcept

   return value;
   }

/* Let the Server know that we are about to exit */

void NotifyServer(char iserver)
   {
   RpcTryExcept
      {
      TerminatePrimeServer(BindingHandle[iserver],
         PrimeServerHandle[iserver]);
      }
   RpcExcept(1)
      {
      return;
      }
   RpcEndExcept
   }

void thread_client()
   {
   unsigned char no_active =0;

   while(1)
      {
      char Buffer[STRING_LENGTH];
      int  i;

      if(VK_ESCAPE == get_character_no_wait())
         {
         EnterCriticalSection(&GlobalCriticalSection);
         Sleep(WAIT);
         for(i = 0;i < ipszNetAdd; i++)
            NotifyServer((char)i);
         clearscreen(0);
         exit(0);
         LeaveCriticalSection(&GlobalCriticalSection);
         }

      mxyputs(58, 2, "Number of Primes", WHITE_ON_CYAN);
      sprintf(Buffer, "%s:%5d", "LOCAL  ", NoPrimeLocal);
      mxyputs((unsigned char)(60), (unsigned char)(4), Buffer,
         WHITE_ON_CYAN);
         {
         no_active = 0;
         EnterCriticalSection(&GlobalCriticalSection);
         for(i = 0; i < ipszNetAdd; i++)
            if(IsActiveServer[i])
               {
               sprintf(Buffer, " %7s:%5d ", pszRemoteName[i],
                  NoPrimeRemote[i]);
               mxyputs(59, (unsigned char)(4+1+no_active), Buffer,
                  RED_ON_CYAN);
               no_active++;
               }
         box(58, 3, 74, (unsigned char)(5 + no_active), SINGLE);
         mxyputc(58, (unsigned char)(6 + no_active), (char)32, 17,
            CYAN_ON_CYAN);
         }

      no_active = 0;
      for(i = 0; i < ipszNetAdd; i++)
         if(!IsActiveServer[i])
            {
            sprintf(Buffer, " %7s:%5d ", pszRemoteName[i], NoPrimeRemote[i]);
            mxyputs(59, (unsigned char)(4+15+no_active), Buffer,
               RED_ON_CYAN);
            no_active ++;
            }

      if(no_active)
         {
         mxyputs(58, (unsigned char)(17), "Inactive Servers",
            WHITE_ON_CYAN);
         box(58, (unsigned char)(18), 74, (unsigned char)(19 + no_active),
            SINGLE);
         mxyputc(58, (unsigned char)(20 + no_active), (char)32, 17,
            CYAN_ON_CYAN);
         }
      else
         for(i = 0; i < 5; i++)
            mxyputc(58, (unsigned char)(17 + no_active + i), (char)32, 17,
               CYAN_ON_CYAN);

      LeaveCriticalSection(&GlobalCriticalSection);
      CurrTime = (GetTickCount() - StartTime) / 1000;
      sprintf(Buffer, "Primes = %d", NoPrimeLocal + NoPrimeRemoteT);
      mxyputs(58, (unsigned char)(7 + ipszNetAdd), Buffer, WHITE_ON_CYAN);
      sprintf(Buffer, "Time = %d.%02d min.", CurrTime/60, CurrTime%60);
      mxyputs(58, (unsigned char)(8 + ipszNetAdd), Buffer, WHITE_ON_CYAN);

      sprintf(Buffer,"First = %d", StartNumber);
      mxyputs(58,(unsigned char)(1+5+3+ipszNetAdd), Buffer, WHITE_ON_CYAN);
      }
   }

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}